package com.cc.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.cc.constant.BusinessStatusConstant;
import com.cc.entity.FileEntity;
import com.cc.mapper.FileMapper;
import com.cc.service.FileService;
import com.cc.util.*;
import com.github.tobato.fastdfs.domain.fdfs.StorePath;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.*;

/**
 * @author CHENCHEN
 * @company 北京学佳澳教育科技
 * @create 2023-03-23 13:06
 */
@Slf4j
@Component
public class SliceUploadHelper {

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private FastDFSUtil fastDFSUtil;

    private final ConcurrentHashMap<String, Object> fileLocks = new ConcurrentHashMap<>();

    @Value("${file-service.default-group}")
    private String DEFAULT_GROUP;

    @Value("${file-service.path-key-prefix}")
    private String PATH_KEY_PREFIX;

    @Value("${file-service.path-full-key-prefix}")
    private String PATH_FULL_KEY_PREFIX;

    @Value("${file-service.upload-no-key-prefix}")
    private String UPLOAD_NO_KEY_PREFIX;

    @Value("${file-service.upload-size-key-prefix}")
    private String UPLOAD_SIZE_KEY_PREFIX;

    @Value("${file-service.slice-size}")
    private int SLICE_SIZE;

    public String uploadFileBySlices(MultipartFile file, Integer sliceNo, Integer totalSliceNo, String fileMd5) throws Exception {
        String pathKeyStr = PATH_KEY_PREFIX + fileMd5;
        String pathFullKeyStr = PATH_FULL_KEY_PREFIX + fileMd5;
        String uploadNoKeyStr = UPLOAD_NO_KEY_PREFIX + fileMd5;
        String uploadSizeKeyStr = UPLOAD_SIZE_KEY_PREFIX + fileMd5;
        String uploadSizeKey = redisUtil.get(uploadSizeKeyStr);
        Long uploadSize = 0L;
        if (!StringUtils.isEmpty(uploadSizeKey)) {
            uploadSize = Long.valueOf(uploadSizeKey);
        }
        String path = redisUtil.get(pathKeyStr);
        fileLocks.putIfAbsent(fileMd5, new Object());
        synchronized (fileLocks.get(fileMd5)) {
            // 在这里执行分片文件的上传操作
            if (StringUtils.isEmpty(path)) {
                StorePath storePath = fastDFSUtil.uploadAppendFile(file, DEFAULT_GROUP);
                path = storePath.getPath();
                redisUtil.set(pathFullKeyStr, storePath.getFullPath());
                redisUtil.set(pathKeyStr, path);
            }else{
                fastDFSUtil.modifyFile(DEFAULT_GROUP, path, file, uploadSize);
            }
        }
        redisUtil.increment(uploadNoKeyStr);
        redisUtil.set(uploadNoKeyStr, String.valueOf(sliceNo));
        uploadSize += file.getSize();
        redisUtil.set(uploadSizeKeyStr, String.valueOf(uploadSize));
        String resultPath = "";
        if (redisUtil.get(uploadNoKeyStr).equals(String.valueOf(totalSliceNo))) {
            resultPath = redisUtil.get(pathFullKeyStr);
            List<String> keyList = Arrays.asList(pathKeyStr, pathFullKeyStr, uploadNoKeyStr, uploadSizeKeyStr);
            redisUtil.deleteKeys(keyList);
            log.info("上传结束 路径为 ： [{}] ", resultPath);
        }
        return resultPath;
    }

    public String convertMultiFileBySlices(MultipartFile multipartFile, String fileMd5) throws Exception {
        // 获取文件名、前缀和后缀
        String filename = multipartFile.getOriginalFilename();
        int index = filename.lastIndexOf(".");
        String name = filename.substring(0, index);
        String fileType = FileUtil.getFileType(multipartFile);
        // 将MultipartFile转换为File
        File tmpFile = FileUtil.convertMultipartFileToFile(multipartFile, name, "." + fileType);

        int uploadedSlices = 0;
        if (redisUtil.hasKey(fileMd5 + "_slices")) {
            // 从Redis中获取已生成的分片文件信息
            uploadedSlices = redisUtil.hLen(fileMd5 + "_slices").intValue();
        }
        // 使用临时目录存储切片文件
        //Path tempDir = Files.createTempDirectory("filetmp");
        String tmpPath = "E:\\tmpfile\\" + name + "\\";
        // 将文件从断点处开始切片，并将切片文件存储在List<File>中
        List<File> slices = FileUtil.writeFileSlices(tmpFile, tmpPath, SLICE_SIZE, uploadedSlices, redisUtil, fileMd5);
        // 删除临时文件
        tmpFile.delete();
        // 依次上传分片文件
        int totalSliceNo = slices.size();
        // 创建一个固定大小的线程池
        ExecutorService executor = Executors.newFixedThreadPool(1);
        CompletionService<String> completionService = new ExecutorCompletionService<>(executor);
        //StorePath storePath = null;
        for (int sliceNo = 1; sliceNo <= totalSliceNo; sliceNo++) {
            File slice = slices.get(sliceNo - 1);
            try {
                MultipartFile multipartSlice = new MyMultipartFile(slice);
                int finalSliceNo = sliceNo;
                completionService.submit(() -> {
                    try {
                        return uploadFileBySlices(multipartSlice, finalSliceNo, totalSliceNo, fileMd5);
                    } catch (Exception e) {
                        throw new ConditionException(BusinessStatusConstant.BUSINESS_EXCEPTION, "Error uploading file slice: " + e.getMessage());
                    } finally {
                        // 删除分片文件
                        slice.delete();
                    }
                });
            } catch (Exception e) {
                throw new ConditionException(BusinessStatusConstant.BUSINESS_EXCEPTION, e.getMessage());
            }
        }
        // 等待所有异步任务完成
        String finalResult = "";
        for (int i = 1; i <= totalSliceNo; i++) {
            try {

                finalResult = completionService.take().get();
                log.info("第 " + i + " 次 获取任务完成结果 ==================== > [{}]",finalResult);
            } catch (ExecutionException | InterruptedException e) {
                throw new ConditionException(BusinessStatusConstant.BUSINESS_EXCEPTION, "Error uploading file slices: " + e.getMessage());
            }
        }
        // 关闭线程池
        executor.shutdown();
        redisUtil.delete(fileMd5 + "_slices");
        return finalResult;
    }
}
